﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using SuperX.Common.Log;
using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Config;
using SuperX.SuperSocket.Socket.Command;
using SuperX.SuperSocket.Socket.Message;

namespace SuperX.SuperSocket.Socket.Server
{
    public class SocketServer : ISockectServer
    {
        public SuperXSocketServer TcpServerEngine { get; set; }
        public ConcurrentDictionary<string, Session> OnLineConnections = new ConcurrentDictionary<string, Session>();
        private ConcurrentDictionary<string, DateTime> _heartBeatManager = new ConcurrentDictionary<string, DateTime>();

        private readonly ServerConfig _serverConfig;
        private readonly SocketServerConfig socketServerConfig;
        private readonly int _maxHeartBeatInterval;//允许的最大时间心跳间隔，超过时将发出通知
        private Timer _timeHeartBeatCheck = null;
        private const int HeartBeatCheckTime = 3000;//心跳查询的时间间隔

        public event EventHandler<MessageArgs> MessageRecevied;

        public event EventHandler<SocketErrorArgs> Error;

        public event SessionHandler<Session, CloseReason> SessionClosed;

        public event SessionHandler<Session> NewSessionConnected;

        private WorkThreadManager workThreadManager;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="superSocketServerConfig"></param>
        public SocketServer(SocketServerConfig superSocketServerConfig)
        {
            if (!superSocketServerConfig.IsSyncInvoke)
            {
                workThreadManager = new WorkThreadManager(this);
            }
            socketServerConfig = superSocketServerConfig;
            _serverConfig = new ServerConfig
            {
                Ip = superSocketServerConfig.Ip,
                Port = superSocketServerConfig.ListenPort,
                Mode = superSocketServerConfig.Mode,
                ServerType = superSocketServerConfig.ServerTypeName,
                Name = superSocketServerConfig.Name,
                SyncSend = superSocketServerConfig.SyncSend,

                ClearIdleSession = superSocketServerConfig.ClearIdleSession,
                ClearIdleSessionInterval = superSocketServerConfig.ClearIdleSessionInterval,
                IdleSessionTimeOut = superSocketServerConfig.IdleSessionTimeOut,

                MaxConnectionNumber = superSocketServerConfig.MaxConnectionNumber,
                MaxRequestLength = superSocketServerConfig.MaxRequestLength,

                ReceiveBufferSize = superSocketServerConfig.ReceiveBufferSize,
                SendBufferSize = superSocketServerConfig.SendBufferSize,
                SendTimeOut = superSocketServerConfig.SendTimeOut
            };
            var textEncoding = superSocketServerConfig.TextEncoding;
            _serverConfig.TextEncoding = textEncoding != DataFormat.UTF8
                ? textEncoding.ToString()
                : "UTF-8";
            _serverConfig.LogFactory = "ConsoleLogFactory";
            _maxHeartBeatInterval = superSocketServerConfig.MaxHeartBeatInterval;
        }

        /// <summary>
        /// 接收客户端信息处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TcpServerEngine_ObjectMessageReceived(object sender, MessageEventArgs e)
        {
            if (_serverConfig.Port != e.AppSession.LocalEndPoint.Port)
            {
                return;
            }
            if (e.SocketMessage.Header != null && e.SocketMessage.Header == "HeartBeat")
            {
                if (_heartBeatManager.Count == 0)
                {
                    return;
                }

                if (!_heartBeatManager.ContainsKey(e.AppSession.SessionID))
                {
                    return;
                }

                if (Encoding.UTF8.GetString(e.SocketMessage.Body) == InnerMessage.Alive.ToString())
                {
                    _heartBeatManager[e.AppSession.SessionID] = e.AppSession.LastActiveTime;//更新时间
                }
                return;
            }
            if (e.SocketMessage.IsError)
            {
                Logger.Warn(e.SocketMessage.Error);
            }
            if (socketServerConfig.IsEnableHeartBeat)
            {
                _heartBeatManager[e.AppSession.SessionID] = e.AppSession.LastActiveTime;//更新时间
            }

            var message = new MessageArgs(e) { AppMessage = new XSocketMessage() { Datas = e.SocketMessage.Body } };

            if (!socketServerConfig.IsSyncInvoke)
            {
                workThreadManager.AddMessage(e.AppSession.SessionID, message);
            }
            else
            {
                FireMessageInvoke(message);
            }
        }

        /// <summary>
        /// 触发SSocket服务信息接收事件，向上一层传递
        /// </summary>
        /// <param name="message"></param>
        internal void FireMessageInvoke(MessageArgs message)
        {
            try
            {
                MessageRecevied?.Invoke(this, message);
            }
            catch (Exception ex)
            {
                Logger.ErrorException(ex);
            }
        }

        /// <summary>
        /// 检查每一个会话上一次心跳时间与当前时间的时间间隔
        /// </summary>
        /// <param name="state"></param>
        private void HeartBeatCheckCallBack(object state)
        {
            try
            {
                _timeHeartBeatCheck.Change(Timeout.Infinite, Timeout.Infinite);
                if (_heartBeatManager.Count == 0)
                {
                    return;
                }

                var nowTime = new TimeSpan(DateTime.Now.Ticks); //获取当前时间的刻度数
                var needCloseSessionIds = new List<string>();
                foreach (var item in _heartBeatManager)
                {
                    TimeSpan intervalTime = DateTime.Now - item.Value;//获取两个时间段绝对时间差
                    if (intervalTime.Seconds > _maxHeartBeatInterval)
                    {
                        needCloseSessionIds.Add(item.Key);
                    }
                }
                foreach (var sessionId in needCloseSessionIds)
                {
                    var session = GetSession(sessionId);
                    if (session != null && session.Connected)
                    {
                        Logger.Error($"{session.SessionID} need close / {_heartBeatManager[session.SessionID].ToString("yyyy-MM-dd HH:mm.ss.fff")}");
                        session.Close();
                        SessionClosed?.Invoke(session, CloseReason.ServerClosing);
                    }
                    OnLineConnections.TryRemove(session.SessionID, out _);
                    _heartBeatManager.TryRemove(session.SessionID, out _);
                }
            }
            catch (Exception ex)
            {
                Logger.Exception(ex);
            }
            finally
            {
                _timeHeartBeatCheck.Change(HeartBeatCheckTime, HeartBeatCheckTime);
            }
        }

        /// <summary>
        /// 当客户端会话断开时的操作
        /// </summary>
        /// <param name="session"></param>
        /// <param name="value"></param>
        private void TcpServerEngine_SessionClosed(Session session, CloseReason value)
        {
            if (!socketServerConfig.IsSyncInvoke)
            {
                workThreadManager?.CloseThread(session.SessionID);
            }

            Logger.Info($"From Server: {session.RemoteEndPoint} 客户端已断开!");
            OnLineConnections.TryRemove(session.SessionID, out _);
            _heartBeatManager.TryRemove(session.SessionID, out _);
            SessionClosed?.Invoke(session, value);
        }

        /// <summary>
        /// 当客户端连接上服务器时的操作
        /// </summary>
        /// <param name="session"></param>
        private void TcpServerEngine_NewSessionConnected(Session session)
        {
            Logger.Info($"From Server :{session.RemoteEndPoint} 的客户端已连接!");
            OnLineConnections.TryAdd(session.SessionID, session);
            if (socketServerConfig.IsEnableHeartBeat && !_heartBeatManager.ContainsKey(session.SessionID))
            {
                _heartBeatManager.TryAdd(session.SessionID, DateTime.Now);
            }

            if (!socketServerConfig.IsSyncInvoke)
            {
                workThreadManager?.CreateThread(session.SessionID);
            }

            NewSessionConnected?.Invoke(session);
        }

        public Session GetSession(string sessionId)
        {
            var flag = OnLineConnections.TryGetValue(sessionId, out var session);
            if (!flag)
            {
                return null;
            }

            return session;
        }

        public bool Start()
        {
            try
            {
                //采用ui界面配置参数的形式
                TcpServerEngine = new SuperXSocketServer();
                if (TcpServerEngine.Setup(_serverConfig, logFactory: new NLogFactory()))
                {
                    Logger.Info("SuperX-SSocket服务器初始化成功!");
                }
                else
                {
                    Logger.Error("SuperX-SSocket服务器初始化失败!");
                    return false;
                }
                if (!TcpServerEngine.Start())
                {
                    Logger.Error("SuperX-SSocket服务器启动失败!端口可能已被占用!");
                    return false;
                }
                Logger.Info("SuperX-SSocket服务器启动成功!");
                if (TcpServerEngine != null)
                {
                    TcpServerEngine.NewSessionConnected += TcpServerEngine_NewSessionConnected;
                    CommandTransfer.MessageReceived += TcpServerEngine_ObjectMessageReceived;
                    TcpServerEngine.SessionClosed += TcpServerEngine_SessionClosed;//会话关闭时的事件
                    if (socketServerConfig.IsEnableHeartBeat)
                    {
                        _timeHeartBeatCheck = new Timer(HeartBeatCheckCallBack, null, HeartBeatCheckTime, HeartBeatCheckTime);//每5秒检查一次每个会话的心跳
                    }
                }
                else
                {
                    Logger.Trace("请检查配置文件中是否有可用的服务信息!");
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                Logger.Exception(ex);
                return false;
            }
        }

        public void Stop()
        {
            workThreadManager?.Close();
            CommandTransfer.MessageReceived -= TcpServerEngine_ObjectMessageReceived;
            foreach (var session in OnLineConnections.Values)
            {
                session.Close();
            }
            _heartBeatManager.Clear();
            OnLineConnections.Clear();
            _timeHeartBeatCheck?.Dispose();
            if (TcpServerEngine == null)
            {
                return;
            }

            TcpServerEngine.NewSessionConnected -= TcpServerEngine_NewSessionConnected;
            TcpServerEngine.SessionClosed -= TcpServerEngine_SessionClosed;
            TcpServerEngine.Stop();
            TcpServerEngine.Dispose();
            Logger.Info("SuperX-SSocket服务器关闭!");
        }

        public void Send(string sessionId, byte[] datas)
        {
            try
            {
                var session = GetSession(sessionId);
                if (session == null || !session.Connected)
                {
                    Logger.Info("From Server:Target client is offline!");
                }
                else
                {
                    session.Send(datas, 0, datas.Length);
                }
            }
            catch (Exception ex)
            {
                Logger.Exception(ex);
            }
        }

        public void Send(string sessionId, string content)
        {
            Send(sessionId, null, content);
        }

        public void Send(string sessionId, byte[] datas, Dictionary<string, string> headers)
        {
            try
            {
                var session = GetSession(sessionId);
                if (session == null || !session.Connected)
                {
                    Logger.Info("From Server:Target client is offline!");
                }
                else
                {
                    var socketMessage = new SocketMessage() { Headers = headers, Header = CommandTransfer.Serialize(headers), Command = "SendObjectWithHeaders", Body = datas };
                    session.SendMessage(socketMessage);
                }
            }
            catch (Exception ex)
            {
                Logger.Exception(ex);
            }
        }

        public void Send(string sessionId, string content, Dictionary<string, string> headers)
        {
            var session = GetSession(sessionId);
            if (session == null || !session.Connected)
            {
                Logger.Info("From Server:Target client is offline!");
                return;
            }
            session.SendWithHeaders(headers, content);
        }

        public void Send(string sessionId, string header, string content)
        {
            var session = GetSession(sessionId);
            if (session == null || !session.Connected)
            {
                Logger.Info("From Server:Target client is offline!");
                return;
            }
            session.SendWithHeader(header, content);
        }

        public void Send<T>(string sessionId, T obj) where T : new()
        {
            var session = GetSession(sessionId);
            if (session == null || !session.Connected)
            {
                Logger.Info("From Server:Target client is offline!");
                return;
            }
            var appMsg = new XSocketMessage<T>(obj);
            session.SendMessage(new SocketMessage() { Body = appMsg.Datas });
        }

        /// <summary>
        /// 服务器的广播
        /// </summary>
        public void Broadcast<T>(T obj) where T : new()
        {
            if (OnLineConnections.Count == 0)
            {
                return;
            }

            var sessionIds = OnLineConnections.Keys.ToList();
            foreach (var sessionId in sessionIds)
            {
                Send(sessionId, obj);
            }
        }
    }
}